# -*- coding: utf-8 -*-

import wiringpi as gpio
import spidev
from PIL import Image, ImageDraw, ImageFont, ImageOps
from datetime import datetime
import locale

locale.setlocale(locale.LC_CTYPE, 'zh_CN.GBK')


EPD_WIDTH       = 128
EPD_HEIGHT      = 296

# EPD2IN9 commands
DRIVER_OUTPUT_CONTROL                       = 0x01
BOOSTER_SOFT_START_CONTROL                  = 0x0C
GATE_SCAN_START_POSITION                    = 0x0F
DEEP_SLEEP_MODE                             = 0x10
DATA_ENTRY_MODE_SETTING                     = 0x11
SW_RESET                                    = 0x12
TEMPERATURE_SENSOR_CONTROL                  = 0x1A
MASTER_ACTIVATION                           = 0x20
DISPLAY_UPDATE_CONTROL_1                    = 0x21
DISPLAY_UPDATE_CONTROL_2                    = 0x22
WRITE_RAM                                   = 0x24
WRITE_VCOM_REGISTER                         = 0x2C
WRITE_LUT_REGISTER                          = 0x32
SET_DUMMY_LINE_PERIOD                       = 0x3A
SET_GATE_TIME                               = 0x3B
BORDER_WAVEFORM_CONTROL                     = 0x3C
SET_RAM_X_ADDRESS_START_END_POSITION        = 0x44
SET_RAM_Y_ADDRESS_START_END_POSITION        = 0x45
SET_RAM_X_ADDRESS_COUNTER                   = 0x4E
SET_RAM_Y_ADDRESS_COUNTER                   = 0x4F
TERMINATE_FRAME_READ_WRITE                  = 0xFF


class EPD:
  def __init__(self, dc, busy, rst, width=196, height=296):
    self.dc = dc
    self.busy = busy
    self.rst = rst
    self.width = width
    self.height = height
    self.spi = spidev.SpiDev(0, 0)
    self.spi.max_speed_hz = 2000000
    self.spi.mode = 0x00

  lut_full_update = [
    0x02, 0x02, 0x01, 0x11, 0x12, 0x12, 0x22, 0x22, 
    0x66, 0x69, 0x69, 0x59, 0x58, 0x99, 0x99, 0x88, 
    0x00, 0x00, 0x00, 0x00, 0xF8, 0xB4, 0x13, 0x51, 
    0x35, 0x51, 0x51, 0x19, 0x01, 0x00
  ]

  lut_partial_update  = [
    0x10, 0x18, 0x18, 0x08, 0x18, 0x18, 0x08, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x13, 0x14, 0x44, 0x12, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00
  ]

  def send_command(self, command):
    gpio.digitalWrite(self.dc, gpio.LOW)
    #gpio.wiringPiSPIDataRW(0, bytes([command]))
    self.spi.writebytes([command])

  def send_data(self, data):
    gpio.digitalWrite(self.dc, gpio.HIGH)
    #gpio.wiringPiSPIDataRW(0, bytes([data]))
    self.spi.writebytes([data])

  def reset(self):
    gpio.digitalWrite(self.rst, gpio.LOW)
    gpio.delay(200)
    gpio.digitalWrite(self.rst, gpio.HIGH)
    gpio.delay(200)

  def is_busy(self):
    return gpio.digitalRead(self.busy) == 1

  def wait_until_idle(self):
    while self.is_busy():
      gpio.delay(100)

  def init(self, lut=None):
    gpio.wiringPiSetupPhys()
    gpio.pinMode(self.dc, gpio.OUTPUT)
    gpio.pinMode(self.busy, gpio.INPUT)
    gpio.pinMode(self.rst, gpio.OUTPUT)
    self.reset()
    self.send_command(DRIVER_OUTPUT_CONTROL)
    self.send_data((EPD_HEIGHT - 1) & 0xFF)
    self.send_data(((EPD_HEIGHT - 1) >> 8) & 0xFF)
    self.send_data(0x00)                     # GD = 0 SM = 0 TB = 0
    self.send_command(BOOSTER_SOFT_START_CONTROL)
    self.send_data(0xD7)
    self.send_data(0xD6)
    self.send_data(0x9D)
    self.send_command(WRITE_VCOM_REGISTER)
    self.send_data(0xA8)                     # VCOM 7C
    self.send_command(SET_DUMMY_LINE_PERIOD)
    self.send_data(0x1A)                     # 4 dummy lines per gate
    self.send_command(SET_GATE_TIME)
    self.send_data(0x08)                     # 2us per line
    self.send_command(DATA_ENTRY_MODE_SETTING)
    self.send_data(0x03)
    self.send_command(WRITE_LUT_REGISTER)
    lut = self.lut_full_update if not lut else lut
    for d in self.lut_full_update:
      self.send_data(d)

  def clear(self, color=0xFF):
    self.set_area(0, 0, self.width-1, self.height-1)
    self.set_pointer(0, 0)
    self.send_command(WRITE_RAM)
    for i in range(int(self.width/8*self.height)):
      self.send_data(color)

  def set_area(self, x_start, y_start, x_end, y_end):
    self.send_command(SET_RAM_X_ADDRESS_START_END_POSITION)
    self.send_data(x_start >> 3 & 0xFF)
    self.send_data(x_end >> 3 & 0xFF)
    self.send_command(SET_RAM_Y_ADDRESS_START_END_POSITION)
    self.send_data(y_start & 0xFF)
    self.send_data(y_start >> 8 & 0xFF)
    self.send_data(y_end & 0xFF)
    self.send_data(y_end >> 8 & 0xFF)

  def set_pointer(self, x, y):
    self.send_command(SET_RAM_X_ADDRESS_COUNTER)
    self.send_data(x >> 3 & 0xFF)
    self.send_command(SET_RAM_Y_ADDRESS_COUNTER)
    self.send_data(y & 0xFF)
    self.send_data(y >> 8 & 0xFF)
    self.wait_until_idle()

  def set_memory(self, image, x, y):
    image = image.convert('1')
    img_w, img_h = image.size
    x = x & 0xF8
    img_w = img_w & 0xF8
    x_end = self.width-1 if x+img_w>=self.width else x+img_w-1
    y_end = self.height-1 if y+img_h>=self.height else x+img_h-1
    self.set_area(x, y, x_end, y_end)
    self.set_pointer(x, y)
    self.send_command(WRITE_RAM)
    pixels = image.load()
    print(x, y, x_end, y_end)
    for i in range(y_end-y+1):
      data = 0
      for j in range(x_end-x+1):
        if pixels[j, i] != 0:
          data |= 0x80 >> (j%8)
        if j % 8 == 7:
          self.send_data(data)
          data = 0
  
  def display(self):
    self.send_command(DISPLAY_UPDATE_CONTROL_2)
    self.send_data(0xC4)
    self.send_command(MASTER_ACTIVATION)
    self.send_command(TERMINATE_FRAME_READ_WRITE)
    self.wait_until_idle()

if __name__ == '__main__':
  black = 0x00
  white = 0xFF
  epd = EPD(11, 12, 13)
  epd.init()
  epd.clear()
  epd.display()
  gpio.delay(2000)
  image = Image.open('../python/monocolor.bmp')
  epd.set_memory(image, 0, 0)
  epd.display()
  gpio.delay(2000)

  epd.init(epd.lut_partial_update)
  epd.reset()
  epd.clear()
  epd.display()
  im = Image.new('1', (296, 128), 255)
  draw = ImageDraw.Draw(im)
  im_w, im_h = im.size
  draw.rectangle((0, 0, im_w, im_h-48), fill=white)
  draw.line([0, 31, 295, 31], fill=black, width=2)
  draw.line([98, 0, 98, 31],fill=black, width=1)
  draw.line([196, 0, 196, 31],fill=black, width=1)
  draw.rectangle([0, im_h-48, 295, im_h], fill=black)

  today = datetime.today()

  grid_w = im_w//3
  grid_h = im_h//4

  yearfont = ImageFont.truetype('msyhsb.ttc', 20, encoding='unic')
  year = u'{}年'.format(today.year)
  year_w, year_h = yearfont.getsize(year)
  draw.text(((grid_w-year_w)//2, 0), year, font=yearfont, fill=black)

  datefont = ImageFont.truetype('msyhsb.ttc', 20, encoding='unic')
  date = u'{}月{}日'.format(today.month, today.day)
  date_w, date_h = datefont.getsize(date)
  draw.text(((grid_w-date_w)//2+grid_w, 0), date, font=datefont, fill=black)

  time = today.strftime('%I:%M %p')
  timefont = ImageFont.truetype('msyhsb.ttc', 42)
  time_w, time_h = timefont.getsize(time)
  draw.text(((im_w-time_w)//2, (48-time_h)//2+31-5), time, font=timefont)

  iconfont = ImageFont.truetype('msyhsb.ttc', 32)
  raspi = 'Raspberry Pi'
  raspi_w, raspi_h = iconfont.getsize(raspi)
  box = ((im_w-48-raspi_w-2)//2, im_h-48, (im_w-48-raspi_w-2)//2+48, im_h)
  im2 = Image.open('raspi.png').convert('L')
  im2 = im2.resize((48, 48), Image.ANTIALIAS)
  im2 = ImageOps.invert(im2).convert('1')

  im.paste(im2, box)
  draw.text(((im_w-48-raspi_w-2)//2+48, im_h-50), raspi, font=iconfont, fill=white)
  im = im.transpose(Image.ROTATE_270)
  epd.set_memory(im, 0, 0)
  epd.display()
  epd.set_memory(im, 0, 0)
  epd.display()

  sec_im = Image.new('1', (grid_w-1, grid_h-1), 0xff)
  draw = ImageDraw.Draw(sec_im)
  font = ImageFont.truetype('msyhsb.ttc', 20)
  while True:
    second = u'{}秒'.format(datetime.now().second)
    print(second)
    draw.rectangle((0, 0, grid_w-1, grid_h-1), fill=white)
    draw.text((0, 0), second, font=font, fill=black)
    epd.set_memory(sec_im.transpose(Image.ROTATE_270), 0, 0)
    epd.display()
  
